package cn.iocoder.yudao.framework.base.common.lock;

import cn.iocoder.yudao.framework.base.common.cache.CacheOperator;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import lombok.Getter;

import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

public class CacheConcurrentLock implements Lock {
    private CacheOperator cache;

    @Getter
    private final String key;
    @Getter
    private final int leaseMilliseconds;

    private final ThreadLocal<String> valueThreadLocal = new ThreadLocal<>();

    public CacheConcurrentLock(CacheOperator cache, int leaseMilliseconds) {
        this(cache, "lock:" + UUID.randomUUID().toString(), leaseMilliseconds);
    }

    public CacheConcurrentLock(CacheOperator cache, String key, int leaseMilliseconds) {
        if (leaseMilliseconds <= 0) {
            throw new IllegalArgumentException("Parameter 'leaseMilliseconds' must grate then 0: " + leaseMilliseconds);
        }
        this.cache = cache;
        this.key = key;
        this.leaseMilliseconds = leaseMilliseconds;
    }


    public void lock() {
        while (!tryLock()) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // Ignore
            }
        }
    }


    public void lockInterruptibly() throws InterruptedException {
        while (!tryLock()) {
            Thread.sleep(1000);
        }
    }


    public boolean tryLock() {
        String value = valueThreadLocal.get();
        if (value == null || value.length() == 0) {
            value = UUID.randomUUID().toString();
            valueThreadLocal.set(value);
        }

        String result = cache.setIfAbsent(key, value, leaseMilliseconds, TimeUnit.MILLISECONDS);
        return StringUtils.equals(result, value);


    }


    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        long waitMs = unit.toMillis(time);
        boolean locked = tryLock();
        while (!locked && waitMs > 0) {
            long sleep = waitMs < 1000 ? waitMs : 1000;
            Thread.sleep(sleep);
            waitMs -= sleep;
            locked = tryLock();
        }
        return locked;
    }


    public void unlock() {
        if (valueThreadLocal.get() != null) {
            // 提示: 必须指定returnType, 类型: 此处必须为Long, 不能是Integer

            cache.delete(key);
            valueThreadLocal.remove();
        }
    }


    public Condition newCondition() {
        throw new UnsupportedOperationException();
    }

    /**
     * 获取当前锁的值
     * return 返回null意味着没有加锁, 但是返回非null值并不以为着当前加锁成功(redis中key可能自动过期)
     */
    public String getLockSecretValue() {
        return valueThreadLocal.get();
    }
}